/* -----------------------------------------------------------------------
 * Title:    minimood.c
 * Author:   Wilhelm Degner aka willi_d
 * Date:     Aug/Sep 2010
 * Hardware: ATtiny13
 *
 * Inspiration from: http://tinkerlog.com/howto/synchronizing-firefly-how-to/
 *
 *            ATtiny13
 *            +----v----+     o Vcc (+5V)
 *       100n |         |     |
 *  gnd --||- + 1     8 +-----+
 *            |         |
 *   SInput --+ 2     7 +-- 100R --|>|--+  Blue
 *            |         |               |
 *         ---+ 3     6 +-- 100R --|>|--+  Green
 *            |         |               |
 *        +---+ 4     5 +-- 100R --|>|--+  Red
 *        |   |         |               |
 *        |   +---------+               |
 *        |                             |
 *       ---                           ---
 *
 *
 */

#define F_CPU 9600000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

#define SInput PB3		  // Serial input, Pin 2
#define R_BIT PB0           // pin 5
#define B_BIT PB2           // pin 7
#define G_BIT PB1           // pin 6

static volatile uint8_t wfsb = 0;   // wait for start bit flag/status
static volatile uint8_t sb = 0;     // serial incoming byte
static volatile uint8_t dataready = 0;  // that's what she said

static volatile uint8_t count = 0; // counter for PWM
static volatile uint8_t r = 0;	// global red value
static volatile uint8_t g = 0;  // global green value
static volatile uint8_t b = 0;  // global blue value
static uint8_t rb = 0; // red buffer for PWM 
static uint8_t gb = 0; // green buffer for PWM
static uint8_t bb = 0; // blue buffer for PWM

/* -----------------------------------------------------
 * ADC interrupt, doing PWM stuff
 *
 * Set global r, g and b to control the color. 
 * Range is for rgb is 0-255 (dark to full power)
 */
SIGNAL(ADC_vect) {

  uint8_t i = 255;  // internal PWM helper register

  // every 256th step take over new values
  if (++count == 0) {    
    rb = r;
    gb = g;
    bb = b;
    // switch on r, g and b
    // even if they should be off
    // this saves two write and some compares,
    // correct values will be writen in the next lines
    PORTB |= 0b00000111;

  }
  // check if switch off r, g and b
  // values are collected into i , write at the end to save cpu-cycles (?)
  if (count == rb) {
	i &= ~(1 << R_BIT); // clear bit for red
  }
  if (count == gb) {
	i &= ~(1 << G_BIT); // clear bit for green
  }
  if (count == bb) {
	i &= ~(1 << B_BIT); // clear bit for blue
  }
  PORTB &= i; // write into outputs

}


/* -----------------------------------------------------
 * Timer0 CompareA interrupt
 * F_CPU 9.600.000 Hz 
 * -> prescaler 8, compared with A ,--> Rate: clk/8 * OCR0A
 *
 *  used OCR0A timings should work well with a 19200 serial interface,
 *  this part has bit errors sometimes and needs more debuging.
 *  smaller OCR0A values result in higher bitrate (could work up 57600)
 */

SIGNAL(TIM0_COMPA_vect) {

  if (wfsb == 0) {					// are we waiting for a new byte?
	if (bit_is_clear(PINB, SInput)) {  // wait for falling edge (startbit)
	  //sb = 0;
	  wfsb++;					// start reading 1st bit
	  OCR0A = 63;				// after skipping startbit
	  }
	else OCR0A = 13;			// smaller timing-windows to not miss the startbit
	}

  else {
	sb = sb/2;  // shift everything right
  	if (bit_is_set(PINB, SInput)) {		// read & set actual bit on the Inputpin
	   sb |= 128;  // MSB setzen
	   }
     OCR0A = 63;
	 if ( ++wfsb == 9 ) {  // already 8 bits read?
	   wfsb = 0;		// wait for startbit next round
	   dataready = 1;	// byte complete
	   // OCR0A = 68;
	   
   }
 }
}


int main(void) {	
	
  uint8_t i = 0;
  uint8_t rd = 0;
  uint8_t gd = 0;
  uint8_t bd = 0;

  // enable pullup
  // PORTB = (1 << SInput);

  // Input Pin
  DDRB &= ~(1 << SInput);

  // enable pins as output
  DDRB |= 
   (1 << R_BIT) |          // R
   (1 << G_BIT) |          // G
   (1 << B_BIT);           // B
 
  // Timer Setup 
  // CTC Mode
  TCCR0A |= (1 << WGM01 );

  // compareA set to 13
  OCR0A = 13 ;

  // timer 0 setup, prescaler clk/8 , comp A on
  TCCR0B = (1 << FOC0A) | (0 << CS02) | (1 << CS01) | (0 << CS00);

  // enable comp A interrupt
  TIMSK0 |= (1 << OCIE0A);	


  ADMUX |= 
    (0 << REFS0) |       // select Vcc as reference
    (1 << ADLAR) |       // ADC output as 8-bit, left adjusted
    (1 << MUX1);         // select channel 2

  // enable adc
  ADCSRA |= 
    (1 << ADEN) |        // enable ADC
    (1 << ADATE) |       // enable auto triggering
    (1 << ADIE) |        // enable ADC interrupt
    (1 << ADSC) |        // start conversion
    (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0);  // prescaler 32 

  // enable all interrupts
  sei();
  
  set_sleep_mode (SLEEP_MODE_IDLE); 

  // intro, blink
  for (i = 0; i < 5; i++) {
    g = 255;
	r = 255;
	b = 255;
    _delay_ms(100);
    g = 0;
	r = 0;
	b = 0;
    _delay_ms(100);
  }


	r=0;
	g=0;
	b=0;

while (1) {
	sleep_mode();			// let's sleep until a interrupt happens
  	if ( dataready == 1)  {
	 if (sb == 0x72 ) {   // ascii r
	   r=r+3;
	   }
	 if (sb == 0x67 ) {   // ascii g
	   g=g+3;
	   }
	 if (sb == 0x62 ) {   // ascii b
	   b=b+3;
	   }
	 if (sb == 0x20 ) {   // ascii <space>
	   r=0;
	   g=0;
	   b=0;
	   }
	dataready = 0;
	}
	
	// other fancy stuff can be done here.

}
  return 0;
	
}
